#include "kbox.h"
#include "epoll_entry.h"

void clearConnItem(ConnItem *item)
{
    item->fd = -1;
    item->rlen = 0;
    item->wlen = 0;
    item->arecv.acceptCallback = NULL;
    item->sendCallback = NULL;
    memset(item->rbuffer, 0, BUFFER_LEN);
    memset(item->wbuffer, 0, BUFFER_LEN);
}

int epfd = 0;
ConnItem connList[2048] = {0}; // 1024 * 1024 = 1048576

void SetNonblocking(int fd)
{
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int SetEvent(int fd, int event, bool addFlag)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = event;
    int ret = 0;
    if (addFlag)
        ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    else
        ret = epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);

    return ret;
}

int AcceptCB(int fd)
{
    struct sockaddr clientAddr;
    memset(&clientAddr, 0, sizeof(struct sockaddr));
    socklen_t len = sizeof(struct sockaddr);
    int clientfd = accept(fd, (struct sockaddr *)&clientAddr, &len);
    // int clientfd = syscall(SYS_accept, fd, (struct sockaddr *)&clientAddr, &len);
    if (clientfd < 0)
        return -1;

    SetNonblocking(clientfd);
    if(SetEvent(clientfd, EPOLLIN | EPOLLET, true) < 0)
    {
        LOG("SetEvent Error Code is %d \n", errno);
        close(clientfd);
        return -1;
    }

    connList[clientfd].fd = clientfd;
    memset(connList[clientfd].rbuffer, 0, BUFFER_LEN);
    connList[clientfd].rlen = 0;
    memset(connList[clientfd].wbuffer, 0, BUFFER_LEN);
    connList[clientfd].wlen = 0;
    connList[clientfd].arecv.recvCallback = RecvCB;
    connList[clientfd].sendCallback = SendCB;

    return 0;
}

int RecvCB(int fd)
{
    memset(connList[fd].rbuffer, 0, BUFFER_LEN);
    char *buffer = connList[fd].rbuffer;
    int len = BUFFER_LEN;

    int count = recv(fd, &buffer[0], len, 0);
    if (count == 0)
    {
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        clearConnItem(&connList[fd]);
        close(fd);
        return 0;
    }
    else if (count < 0)
    {
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        close(fd);
        clearConnItem(&connList[fd]);
        return -1;
    }

    connList[fd].rlen = count;
    KBoxRequest(&connList[fd]);

    SetEvent(fd, EPOLLOUT | EPOLLET, false);

    return count;
}

int SendCB(int fd)
{
    const char *buff = connList[fd].wbuffer;
    int len = (int)connList[fd].wlen;
    int count = send(fd, buff, len, 0);
    SetEvent(fd, EPOLLIN | EPOLLET, false);

    return count;
}

int InitServer(unsigned short port)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        return -1;

    struct sockaddr_in serAddr;
    serAddr.sin_port = htons(port);
    serAddr.sin_family = AF_INET;
    serAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sockfd, (struct sockaddr *)&serAddr, sizeof(serAddr)) < 0)
    {
        close(sockfd);
        return -2;
    }

    if (listen(sockfd, 5) < 0)
    {
        close(sockfd);
        return -3;
    }

    SetNonblocking(sockfd);
    return sockfd;
}

int EpollEntry(void)
{
    int portCount = 20;
    unsigned short port = 8889;

    epfd = epoll_create1(0);

    for (int i = 0; i < 1; i++)
    {
        int sockfd = InitServer(port + i);
        if (sockfd < 0)
        {
            LOG("InitServer Error Code : %d \n", sockfd);
            continue;
        }

        ConnItem item;
        item.fd = sockfd;
        item.arecv.acceptCallback = AcceptCB;
        connList[sockfd] = item;
        SetEvent(sockfd, EPOLLIN | EPOLLET, true);
    }

    struct epoll_event events[1024] = {0};

    while (true)
    {
        int nread = epoll_wait(epfd, events, 1024, -1);
        if (nread < 0)
        {
            LOG("epoll_wait Error Code is %d \n", nread);
            break;
        }

        for (int i = 0; i < nread; i++)
        {
            int fd = events[i].data.fd;
            if (events[i].events & EPOLLIN)
            {
                int count = connList[fd].arecv.recvCallback(fd);
            }
            else if (events[i].events & EPOLLOUT)
            {
                int count = connList[fd].sendCallback(fd);
            }
        }
    }
}